/*!
 * SmartMenus jQuery Plugin - v0.9.6 - March 27, 2014
 * http://www.smartmenus.org/
 *
 * Copyright 2014 Vasil Dinkov, Vadikom Web Ltd.
 * http://vadikom.com
 *
 * Licensed MIT
 */
define(function(require, exports, module){
	var jQuery = require('jquery');
	(function($) {
		var menuTrees = [],
			IE = !!window.createPopup, // we need to detect it, unfortunately
			IElt9 = IE && !document.defaultView,
			IElt8 = IE && !document.querySelector,
			IE6 = IE && typeof document.documentElement.currentStyle.minWidth == 'undefined',
			mouse = false, // optimize for touch by default - we will detect for mouse input
			mouseDetectionEnabled = false;

		// Handle detection for mouse input (i.e. desktop browsers, tablets with a mouse, etc.)
		function initMouseDetection(disable) {
			if (!mouseDetectionEnabled && !disable) {
				// if we get two consecutive mousemoves within 2 pixels from each other and within 300ms, we assume a real mouse/cursor is present
				// in practice, this seems like impossible to trick unintentianally with a real mouse and a pretty safe detection on touch devices (even with older browsers that do not support touch events)
				var firstTime = true,
					lastMove = null;
				$(document).bind({
					'mousemove.smartmenus_mouse': function(e) {
						var thisMove = { x: e.pageX, y: e.pageY, timeStamp: new Date().getTime() };
						if (lastMove) {
							var deltaX = Math.abs(lastMove.x - thisMove.x),
								deltaY = Math.abs(lastMove.y - thisMove.y);
							if ((deltaX > 0 || deltaY > 0) && deltaX <= 2 && deltaY <= 2 && thisMove.timeStamp - lastMove.timeStamp <= 300) {
								mouse = true;
								// if this is the first check after page load, check if we are not over some item by chance and call the mouseenter handler if yes
								if (firstTime) {
									var $a = $(e.target).closest('a');
									if ($a.is('a')) {
										$.each(menuTrees, function() {
											if ($.contains(this.$root[0], $a[0])) {
												this.itemEnter({ currentTarget: $a[0] });
												return false;
											}
										});
									}
									firstTime = false;
								}
							}
						}
						lastMove = thisMove;
					},
					'touchstart.smartmenus_mouse pointerover.smartmenus_mouse MSPointerOver.smartmenus_mouse': function(e) {
						if (!/^(4|mouse)$/.test(e.originalEvent.pointerType)) {
							mouse = false;
						}
					}
				});
				mouseDetectionEnabled = true;
			} else if (mouseDetectionEnabled && disable) {
				$(document).unbind('.smartmenus_mouse');
				mouseDetectionEnabled = false;
			}
		};

		$.SmartMenus = function(elm, options) {
			this.$root = $(elm);
			this.opts = options;
			this.rootId = ''; // internal
			this.$subArrow = null;
			this.subMenus = []; // all sub menus in the tree (UL elms) in no particular order (only real - e.g. UL's in mega sub menus won't be counted)
			this.activatedItems = []; // stores last activated A's for each level
			this.visibleSubMenus = []; // stores visible sub menus UL's
			this.showTimeout = 0;
			this.hideTimeout = 0;
			this.scrollTimeout = 0;
			this.clickActivated = false;
			this.zIndexInc = 0;
			this.$firstLink = null; // we'll use these for some tests
			this.$firstSub = null; // at runtime so we'll cache them
			this.disabled = false;
			this.$disableOverlay = null;
			this.init();
		};

		$.extend($.SmartMenus, {
			hideAll: function() {
				$.each(menuTrees, function() {
					this.menuHideAll();
				});
			},
			destroy: function() {
				while (menuTrees.length) {
					menuTrees[0].destroy();
				}
				initMouseDetection(true);
			},
			prototype: {
				init: function(refresh) {
					var self = this;

					if (!refresh) {
						menuTrees.push(this);

						this.rootId = (new Date().getTime() + Math.random() + '').replace(/\D/g, '');

						if (this.$root.hasClass('sm-rtl')) {
							this.opts.rightToLeftSubMenus = true;
						}

						// init root (main menu)
						this.$root
							.data('smartmenus', this)
							.attr('data-smartmenus-id', this.rootId)
							.dataSM('level', 1)
							.bind({
								'mouseover.smartmenus focusin.smartmenus': $.proxy(this.rootOver, this),
								'mouseout.smartmenus focusout.smartmenus': $.proxy(this.rootOut, this)
							})
							.delegate('a', {
								'mouseenter.smartmenus': $.proxy(this.itemEnter, this),
								'mouseleave.smartmenus': $.proxy(this.itemLeave, this),
								'mousedown.smartmenus': $.proxy(this.itemDown, this),
								'focus.smartmenus': $.proxy(this.itemFocus, this),
								'blur.smartmenus': $.proxy(this.itemBlur, this),
								'click.smartmenus': $.proxy(this.itemClick, this),
								'touchend.smartmenus': $.proxy(this.itemTouchEnd, this)
							});

						var eNamespace = '.smartmenus' + this.rootId;
						// hide menus on tap or click outside the root UL
						if (this.opts.hideOnClick) {
							$(document).bind('touchstart' + eNamespace, $.proxy(this.docTouchStart, this))
								.bind('touchmove' + eNamespace, $.proxy(this.docTouchMove, this))
								.bind('touchend' + eNamespace, $.proxy(this.docTouchEnd, this))
								// for Opera Mobile < 11.5, webOS browser, etc. we'll check click too
								.bind('click' + eNamespace, $.proxy(this.docClick, this));
						}
						// hide sub menus on resize
						$(window).bind('resize' + eNamespace + ' orientationchange' + eNamespace, $.proxy(this.winResize, this));

						if (this.opts.subIndicators) {
							this.$subArrow = $('<span/>').addClass('sub-arrow');
							if (this.opts.subIndicatorsText) {
								this.$subArrow.html(this.opts.subIndicatorsText);
							}
						}

						// make sure mouse detection is enabled
						initMouseDetection();
					}

					// init sub menus
					this.$firstSub = this.$root.find('ul').each(function() { self.menuInit($(this)); }).eq(0);

					this.$firstLink = this.$root.find('a').eq(0);

					// find current item
					if (this.opts.markCurrentItem) {
						var reDefaultDoc = /(index|default)\.[^#\?\/]*/i,
							reHash = /#.*/,
							locHref = window.location.href.replace(reDefaultDoc, ''),
							locHrefNoHash = locHref.replace(reHash, '');
						this.$root.find('a').each(function() {
							var href = this.href.replace(reDefaultDoc, ''),
								$this = $(this);
							if (href == locHref || href == locHrefNoHash) {
								$this.addClass('current');
								if (self.opts.markCurrentTree) {
									$this.parents('li').each(function() {
										var $this = $(this);
										if ($this.dataSM('sub')) {
											$this.children('a').addClass('current');
										}
									});
								}
							}
						});
					}
				},
				destroy: function() {
					this.menuHideAll();
					this.$root
						.removeData('smartmenus')
						.removeAttr('data-smartmenus-id')
						.removeDataSM('level')
						.unbind('.smartmenus')
						.undelegate('.smartmenus');
					var eNamespace = '.smartmenus' + this.rootId;
					$(document).unbind(eNamespace);
					$(window).unbind(eNamespace);
					if (this.opts.subIndicators) {
						this.$subArrow = null;
					}
					var self = this;
					$.each(this.subMenus, function() {
						if (this.hasClass('mega-menu')) {
							this.find('ul').removeDataSM('in-mega');
						}
						if (this.dataSM('shown-before')) {
							if (IElt8) {
								this.children().css({ styleFloat: '', width: '' });
							}
							if (self.opts.subMenusMinWidth || self.opts.subMenusMaxWidth) {
								if (!IE6) {
									this.css({ width: '', minWidth: '', maxWidth: '' }).removeClass('sm-nowrap');
								} else {
									this.css({ width: '', overflowX: '', overflowY: '' }).children().children('a').css('white-space', '');
								}
							}
							if (this.dataSM('scroll-arrows')) {
								this.dataSM('scroll-arrows').remove();
							}
							this.css({ zIndex: '', top: '', left: '', marginLeft: '', marginTop: '', display: '' });
						}
						if (self.opts.subIndicators) {
							this.dataSM('parent-a').removeClass('has-submenu').children('span.sub-arrow').remove();
						}
						this.removeDataSM('shown-before')
							.removeDataSM('ie-shim')
							.removeDataSM('scroll-arrows')
							.removeDataSM('parent-a')
							.removeDataSM('level')
							.removeDataSM('beforefirstshowfired')
							.parent().removeDataSM('sub');
					});
					if (this.opts.markCurrentItem) {
						this.$root.find('a.current').removeClass('current');
					}
					this.$root = null;
					this.$firstLink = null;
					this.$firstSub = null;
					if (this.$disableOverlay) {
						this.$disableOverlay.remove();
						this.$disableOverlay = null;
					}
					menuTrees.splice($.inArray(this, menuTrees), 1);
				},
				disable: function(noOverlay) {
					if (!this.disabled) {
						this.menuHideAll();
						// display overlay over the menu to prevent interaction
						if (!noOverlay && !this.opts.isPopup && this.$root.is(':visible')) {
							var pos = this.$root.offset();
							this.$disableOverlay = $('<div class="sm-jquery-disable-overlay"/>').css({
								position: 'absolute',
								top: pos.top,
								left: pos.left,
								width: this.$root.outerWidth(),
								height: this.$root.outerHeight(),
								zIndex: this.getStartZIndex() + 1,
								opacity: 0
							}).appendTo(document.body);
						}
						this.disabled = true;
					}
				},
				docClick: function(e) {
					// hide on any click outside the menu or on a menu link
					if (this.visibleSubMenus.length && !$.contains(this.$root[0], e.target) || $(e.target).is('a')) {
						this.menuHideAll();
					}
				},
				docTouchEnd: function(e) {
					if (!this.lastTouch) {
						return;
					}
					if (this.visibleSubMenus.length && (this.lastTouch.x2 === undefined || this.lastTouch.x1 == this.lastTouch.x2) && (this.lastTouch.y2 === undefined || this.lastTouch.y1 == this.lastTouch.y2) && (!this.lastTouch.target || !$.contains(this.$root[0], this.lastTouch.target))) {
						if (this.hideTimeout) {
							clearTimeout(this.hideTimeout);
							this.hideTimeout = 0;
						}
						// hide with a delay to prevent triggering accidental unwanted click on some page element
						var self = this;
						this.hideTimeout = setTimeout(function() { self.menuHideAll(); }, 350);
					}
					this.lastTouch = null;
				},
				docTouchMove: function(e) {
					if (!this.lastTouch) {
						return;
					}
					var touchPoint = e.originalEvent.touches[0];
					this.lastTouch.x2 = touchPoint.pageX;
					this.lastTouch.y2 = touchPoint.pageY;
				},
				docTouchStart: function(e) {
					var touchPoint = e.originalEvent.touches[0];
					this.lastTouch = { x1: touchPoint.pageX, y1: touchPoint.pageY, target: touchPoint.target };
				},
				enable: function() {
					if (this.disabled) {
						if (this.$disableOverlay) {
							this.$disableOverlay.remove();
							this.$disableOverlay = null;
						}
						this.disabled = false;
					}
				},
				getHeight: function($elm) {
					return this.getOffset($elm, true);
				},
				// returns precise width/height float values in IE9+, FF4+, recent WebKit
				// http://vadikom.com/dailies/offsetwidth-offsetheight-useless-in-ie9-firefox4/
				getOffset: function($elm, height) {
					var old;
					if ($elm.css('display') == 'none') {
						old = { position: $elm[0].style.position, visibility: $elm[0].style.visibility };
						$elm.css({ position: 'absolute', visibility: 'hidden' }).show();
					}
					var defaultView = $elm[0].ownerDocument.defaultView,
						compStyle = defaultView && defaultView.getComputedStyle && defaultView.getComputedStyle($elm[0], null),
						val = compStyle && parseFloat(compStyle[height ? 'height' : 'width']);
					if (val) {
						val += parseFloat(compStyle[height ? 'paddingTop' : 'paddingLeft'])
							+ parseFloat(compStyle[height ? 'paddingBottom' : 'paddingRight'])
							+ parseInt(compStyle[height ? 'borderTopWidth' : 'borderLeftWidth'])
							+ parseInt(compStyle[height ? 'borderBottomWidth' : 'borderRightWidth']);
					} else {
						val = height ? $elm[0].offsetHeight : $elm[0].offsetWidth;
					}
					if (old) {
						$elm.hide().css(old);
					}
					return val;
				},
				getWidth: function($elm) {
					return this.getOffset($elm);
				},
				getStartZIndex: function() {
					var zIndex = parseInt(this.$root.css('z-index'));
					return !isNaN(zIndex) ? zIndex : 1;
				},
				handleEvents: function() {
					return !this.disabled && this.isCSSOn();
				},
				handleItemEvents: function($a) {
					return this.handleEvents() && !this.isLinkInMegaMenu($a);
				},
				isCollapsible: function() {
					return this.$firstSub.css('position') == 'static';
				},
				isCSSOn: function() {
					return this.$firstLink.css('display') == 'block';
				},
				isFixed: function() {
					return this.$root.css('position') == 'fixed';
				},
				isLinkInMegaMenu: function($a) {
					return !$a.parent().parent().dataSM('level');
				},
				isTouchMode: function() {
					return !mouse || this.isCollapsible();
				},
				itemActivate: function($a) {
					var $li = $a.parent(),
						$ul = $li.parent(),
						level = $ul.dataSM('level');
					// if for some reason the parent item is not activated (e.g. this is an API call to activate the item), activate all parent items first
					if (level > 1 && (!this.activatedItems[level - 2] || this.activatedItems[level - 2][0] != $ul.dataSM('parent-a')[0])) {
						var self = this;
						$($ul.parentsUntil('[data-smartmenus-id]', 'ul').get().reverse()).add($ul).each(function() {
							self.itemActivate($(this).dataSM('parent-a'));
						});
					}
					// hide any visible deeper level sub menus
					if (this.visibleSubMenus.length > level) {
						for (var i = this.visibleSubMenus.length - 1, l = !this.activatedItems[level - 1] || this.activatedItems[level - 1][0] != $a[0] ? level - 1 : level; i > l; i--) {
							this.menuHide(this.visibleSubMenus[i]);
						}
					}
					// save new active item and sub menu for this level
					this.activatedItems[level - 1] = $a;
					this.visibleSubMenus[level - 1] = $ul;
					if (this.$root.triggerHandler('activate.smapi', $a[0]) === false) {
						return;
					}
					// show the sub menu if this item has one
					var $sub = $li.dataSM('sub');
					if ($sub && (this.isTouchMode() || (!this.opts.showOnClick || this.clickActivated))) {
						this.menuShow($sub);
					}
				},
				itemBlur: function(e) {
					var $a = $(e.currentTarget);
					if (!this.handleItemEvents($a)) {
						return;
					}
					this.$root.triggerHandler('blur.smapi', $a[0]);
				},
				itemClick: function(e) {
					var $a = $(e.currentTarget);
					if (!this.handleItemEvents($a)) {
						return;
					}
					$a.removeDataSM('mousedown');
					if (this.$root.triggerHandler('click.smapi', $a[0]) === false) {
						return false;
					}
					var $sub = $a.parent().dataSM('sub');
					if (this.isTouchMode()) {
						// undo fix: prevent the address bar on iPhone from sliding down when expanding a sub menu
						if ($a.dataSM('href')) {
							$a.attr('href', $a.dataSM('href')).removeDataSM('href');
						}
						// if the sub is not visible
						if ($sub && (!$sub.dataSM('shown-before') || !$sub.is(':visible'))) {
							// try to activate the item and show the sub
							this.itemActivate($a);
							// if "itemActivate" showed the sub, prevent the click so that the link is not loaded
							// if it couldn't show it, then the sub menus are disabled with an !important declaration (e.g. via mobile styles) so let the link get loaded
							if ($sub.is(':visible')) {
								return false;
							}
						}
					} else if (this.opts.showOnClick && $a.parent().parent().dataSM('level') == 1 && $sub) {
						this.clickActivated = true;
						this.menuShow($sub);
						return false;
					}
					if ($a.hasClass('disabled')) {
						return false;
					}
					if (this.$root.triggerHandler('select.smapi', $a[0]) === false) {
						return false;
					}
				},
				itemDown: function(e) {
					var $a = $(e.currentTarget);
					if (!this.handleItemEvents($a)) {
						return;
					}
					$a.dataSM('mousedown', true);
				},
				itemEnter: function(e) {
					var $a = $(e.currentTarget);
					if (!this.handleItemEvents($a)) {
						return;
					}
					if (!this.isTouchMode()) {
						if (this.showTimeout) {
							clearTimeout(this.showTimeout);
							this.showTimeout = 0;
						}
						var self = this;
						this.showTimeout = setTimeout(function() { self.itemActivate($a); }, this.opts.showOnClick && $a.parent().parent().dataSM('level') == 1 ? 1 : this.opts.showTimeout);
					}
					this.$root.triggerHandler('mouseenter.smapi', $a[0]);
				},
				itemFocus: function(e) {
					var $a = $(e.currentTarget);
					if (!this.handleItemEvents($a)) {
						return;
					}
					// fix (the mousedown check): in some browsers a tap/click produces consecutive focus + click events so we don't need to activate the item on focus
					if ((!this.isTouchMode() || !$a.dataSM('mousedown')) && (!this.activatedItems.length || this.activatedItems[this.activatedItems.length - 1][0] != $a[0])) {
						this.itemActivate($a);
					}
					this.$root.triggerHandler('focus.smapi', $a[0]);
				},
				itemLeave: function(e) {
					var $a = $(e.currentTarget);
					if (!this.handleItemEvents($a)) {
						return;
					}
					if (!this.isTouchMode()) {
						if ($a[0].blur) {
							$a[0].blur();
						}
						if (this.showTimeout) {
							clearTimeout(this.showTimeout);
							this.showTimeout = 0;
						}
					}
					$a.removeDataSM('mousedown');
					this.$root.triggerHandler('mouseleave.smapi', $a[0]);
				},
				itemTouchEnd: function(e) {
					var $a = $(e.currentTarget);
					if (!this.handleItemEvents($a)) {
						return;
					}
					// prevent the address bar on iPhone from sliding down when expanding a sub menu
					var $sub = $a.parent().dataSM('sub');
					if ($a.attr('href').charAt(0) !== '#' && $sub && (!$sub.dataSM('shown-before') || !$sub.is(':visible'))) {
						$a.dataSM('href', $a.attr('href'));
						$a.attr('href', '#');
					}
				},
				menuFixLayout: function($ul) {
					// fixes a menu that is being shown for the first time
					if (!$ul.dataSM('shown-before')) {
						$ul.hide().dataSM('shown-before', true);
						// fix the layout of the items in IE<8
						if (IElt8) {
							$ul.children().css({ styleFloat: 'left', width: '100%' });
						}
					}
				},
				menuHide: function($sub) {
					if (this.$root.triggerHandler('beforehide.smapi', $sub[0]) === false) {
						return;
					}
					$sub.stop(true, true);
					if ($sub.is(':visible')) {
						var complete = function() {
							// unset z-index
							if (IElt9) {
								$sub.parent().css('z-index', '');
							} else {
								$sub.css('z-index', '');
							}
						};
						// if sub is collapsible (mobile view)
						if (this.isCollapsible()) {
							if (this.opts.collapsibleHideFunction) {
								this.opts.collapsibleHideFunction.call(this, $sub, complete);
							} else {
								$sub.hide(this.opts.collapsibleHideDuration, complete);
							}
						} else {
							if (this.opts.hideFunction) {
								this.opts.hideFunction.call(this, $sub, complete);
							} else {
								$sub.hide(this.opts.hideDuration, complete);
							}
						}
						// remove IE iframe shim
						if ($sub.dataSM('ie-shim')) {
							$sub.dataSM('ie-shim').remove();
						}
						// deactivate scrolling if it is activated for this sub
						if ($sub.dataSM('scroll')) {
							$sub.unbind('.smartmenus_scroll').removeDataSM('scroll').dataSM('scroll-arrows').hide();
						}
						// unhighlight parent item
						$sub.dataSM('parent-a').removeClass('highlighted');
						var level = $sub.dataSM('level');
						this.activatedItems.splice(level - 1, 1);
						this.visibleSubMenus.splice(level - 1, 1);
						this.$root.triggerHandler('hide.smapi', $sub[0]);
					}
				},
				menuHideAll: function() {
					if (this.showTimeout) {
						clearTimeout(this.showTimeout);
						this.showTimeout = 0;
					}
					// hide all subs
					for (var i = this.visibleSubMenus.length - 1; i > 0; i--) {
						this.menuHide(this.visibleSubMenus[i]);
					}
					// hide root if it's popup
					if (this.opts.isPopup) {
						this.$root.stop(true, true);
						if (this.$root.is(':visible')) {
							if (this.opts.hideFunction) {
								this.opts.hideFunction.call(this, this.$root);
							} else {
								this.$root.hide(this.opts.hideDuration);
							}
							// remove IE iframe shim
							if (this.$root.dataSM('ie-shim')) {
								this.$root.dataSM('ie-shim').remove();
							}
						}
					}
					this.activatedItems = [];
					this.visibleSubMenus = [];
					this.clickActivated = false;
					// reset z-index increment
					this.zIndexInc = 0;
				},
				menuIframeShim: function($ul) {
					// create iframe shim for the menu
					if (IE && this.opts.overlapControlsInIE && !$ul.dataSM('ie-shim')) {
						$ul.dataSM('ie-shim', $('<iframe/>').attr({ src: 'javascript:0', tabindex: -9 })
							.css({ position: 'absolute', top: 'auto', left: '0', opacity: 0, border: '0' })
						);
					}
				},
				menuInit: function($ul) {
					if (!$ul.dataSM('in-mega')) {
						this.subMenus.push($ul);
						// mark UL's in mega drop downs (if any) so we can neglect them
						if ($ul.hasClass('mega-menu')) {
							$ul.find('ul').dataSM('in-mega', true);
						}
						// get level (much faster than, for example, using parentsUntil)
						var level = 2,
							par = $ul[0];
						while ((par = par.parentNode.parentNode) != this.$root[0]) {
							level++;
						}
						// cache stuff
						$ul.dataSM('parent-a', $ul.prevAll('a').eq(-1))
							.dataSM('level', level)
							.parent().dataSM('sub', $ul);
						// add sub indicator to parent item
						if (this.opts.subIndicators) {
							$ul.dataSM('parent-a').addClass('has-submenu')[this.opts.subIndicatorsPos](this.$subArrow.clone());
						}
					}
				},
				menuPosition: function($sub) {
					var $a = $sub.dataSM('parent-a'),
						$ul = $sub.parent().parent(),
						level = $sub.dataSM('level'),
						subW = this.getWidth($sub),
						subH = this.getHeight($sub),
						itemOffset = $a.offset(),
						itemX = itemOffset.left,
						itemY = itemOffset.top,
						itemW = this.getWidth($a),
						itemH = this.getHeight($a),
						$win = $(window),
						winX = $win.scrollLeft(),
						winY = $win.scrollTop(),
						winW = $win.width(),
						winH = $win.height(),
						horizontalParent = $ul.hasClass('sm') && !$ul.hasClass('sm-vertical'),
						subOffsetX = level == 2 ? this.opts.mainMenuSubOffsetX : this.opts.subMenusSubOffsetX,
						subOffsetY = level == 2 ? this.opts.mainMenuSubOffsetY : this.opts.subMenusSubOffsetY,
						x, y;
					if (horizontalParent) {
						x = this.opts.rightToLeftSubMenus ? itemW - subW - subOffsetX : subOffsetX;
						y = this.opts.bottomToTopSubMenus ? -subH - subOffsetY : itemH + subOffsetY;
					} else {
						x = this.opts.rightToLeftSubMenus ? subOffsetX - subW : itemW - subOffsetX;
						y = this.opts.bottomToTopSubMenus ? itemH - subOffsetY - subH : subOffsetY;
					}
					if (this.opts.keepInViewport && !this.isCollapsible()) {
						if (this.isFixed()) {
							itemX -= winX;
							itemY -= winY;
							winX = winY = 0;
						}
						var absX = itemX + x,
							absY = itemY + y;
						if (this.opts.rightToLeftSubMenus && absX < winX) {
							x = horizontalParent ? winX - absX + x : itemW - subOffsetX;
						} else if (!this.opts.rightToLeftSubMenus && absX + subW > winX + winW) {
							x = horizontalParent ? winX + winW - subW - absX + x : subOffsetX - subW;
						}
						if (!horizontalParent) {
							if (subH < winH && absY + subH > winY + winH) {
								y += winY + winH - subH - absY;
							} else if (subH >= winH || absY < winY) {
								y += winY - absY;
							}
						}
						// do we need scrolling?
						// 0.49 added for the sake of IE9/FF4+ where we might be dealing with float numbers for "subH"
						if (mouse && (horizontalParent && (absY + subH > winY + winH + 0.49 || absY < winY) || !horizontalParent && subH > winH + 0.49)) {
							var self = this;
							if (!$sub.dataSM('scroll-arrows')) {
								$sub.dataSM('scroll-arrows', $([$('<span class="scroll-up"><span class="scroll-up-arrow"></span></span>')[0], $('<span class="scroll-down"><span class="scroll-down-arrow"></span></span>')[0]])
									.bind({
										mouseenter: function() { self.menuScroll($sub, $(this).hasClass('scroll-up')); },
										mouseleave: function(e) {
											self.menuScrollStop($sub);
											self.menuScrollOut($sub, e);
										},
										'mousewheel DOMMouseScroll': function(e) { e.preventDefault(); }
									})
									.insertAfter($sub)
								);
							}
							// bind events to show/hide arrows on hover and save scrolling data for this sub
							var vportY = winY - (itemY + itemH);
							$sub.dataSM('scroll', {
								vportY: vportY,
								subH: subH,
								winH: winH,
								step: 1
							})
								.bind({
									'mouseover.smartmenus_scroll': function(e) { self.menuScrollOver($sub, e); },
									'mouseout.smartmenus_scroll': function(e) { self.menuScrollOut($sub, e); },
									'mousewheel.smartmenus_scroll DOMMouseScroll.smartmenus_scroll': function(e) { self.menuScrollMousewheel($sub, e); }
								})
								.dataSM('scroll-arrows').css({ top: 'auto', left: '0', marginLeft: x + (parseInt($sub.css('border-left-width')) || 0), width: this.getWidth($sub) - (parseInt($sub.css('border-left-width')) || 0) - (parseInt($sub.css('border-right-width')) || 0), zIndex: this.getStartZIndex() + this.zIndexInc })
								.eq(0).css('margin-top', vportY).end()
								.eq(1).css('margin-top', vportY + winH - this.getHeight($sub.dataSM('scroll-arrows').eq(1))).end()
								.eq(horizontalParent && this.opts.bottomToTopSubMenus ? 0 : 1).show();
						}
					}
					$sub.css({ top: 'auto', left: '0', marginLeft: x, marginTop: y - itemH });
					// IE iframe shim
					this.menuIframeShim($sub);
					if ($sub.dataSM('ie-shim')) {
						$sub.dataSM('ie-shim').css({ zIndex: $sub.css('z-index'), width: subW, height: subH, marginLeft: x, marginTop: y - itemH });
					}
				},
				menuScroll: function($sub, up, wheel) {
					var y = parseFloat($sub.css('margin-top')),
						scroll = $sub.dataSM('scroll'),
						end = scroll.vportY + (up ? 0 : scroll.winH - scroll.subH),
						step = wheel || !this.opts.scrollAccelerate ? this.opts.scrollStep : Math.floor($sub.dataSM('scroll').step);
					$sub.add($sub.dataSM('ie-shim')).css('margin-top', Math.abs(end - y) > step ? y + (up ? step : -step) : end);
					y = parseFloat($sub.css('margin-top'));
					// show opposite arrow if appropriate
					if (up && y + scroll.subH > scroll.vportY + scroll.winH || !up && y < scroll.vportY) {
						$sub.dataSM('scroll-arrows').eq(up ? 1 : 0).show();
					}
					// accelerate when not using mousewheel to scroll
					if (!wheel && this.opts.scrollAccelerate && $sub.dataSM('scroll').step < this.opts.scrollStep) {
						$sub.dataSM('scroll').step += 0.5;
					}
					// "y" and "end" might be float numbers in IE9/FF4+ so this weird way to check is used
					if (Math.abs(y - end) < 1) {
						$sub.dataSM('scroll-arrows').eq(up ? 0 : 1).hide();
						$sub.dataSM('scroll').step = 1;
					} else if (!wheel) {
						var self = this;
						this.scrollTimeout = setTimeout(function() { self.menuScroll($sub, up); }, this.opts.scrollInterval);
					}
				},
				menuScrollMousewheel: function($sub, e) {
					var $closestSub = $(e.target).closest('ul');
					while ($closestSub.dataSM('in-mega')) {
						$closestSub = $closestSub.parent().closest('ul');
					}
					if ($closestSub[0] == $sub[0]) {
						var up = (e.originalEvent.wheelDelta || -e.originalEvent.detail) > 0;
						if ($sub.dataSM('scroll-arrows').eq(up ? 0 : 1).is(':visible')) {
							this.menuScroll($sub, up, true);
						}
					}
					e.preventDefault();
				},
				menuScrollOut: function($sub, e) {
					var reClass = /^scroll-(up|down)/,
						$closestSub = $(e.relatedTarget).closest('ul');
					while ($closestSub.dataSM('in-mega')) {
						$closestSub = $closestSub.parent().closest('ul');
					}
					if (!reClass.test((e.relatedTarget || '').className) && ($sub[0] != e.relatedTarget && !$.contains($sub[0], e.relatedTarget) || $closestSub[0] != $sub[0])) {
						$sub.dataSM('scroll-arrows').css('visibility', 'hidden');
					}
				},
				menuScrollOver: function($sub, e) {
					var reClass = /^scroll-(up|down)/,
						$closestSub = $(e.target).closest('ul');
					while ($closestSub.dataSM('in-mega')) {
						$closestSub = $closestSub.parent().closest('ul');
					}
					if (!reClass.test(e.target.className) && $closestSub[0] == $sub[0]) {
						$sub.dataSM('scroll-arrows').css('visibility', 'visible');
					}
				},
				menuScrollStop: function($sub) {
					if (this.scrollTimeout) {
						clearTimeout(this.scrollTimeout);
						this.scrollTimeout = 0;
						$sub.dataSM('scroll').step = 1;
					}
				},
				menuShow: function($sub) {
					if (!$sub.dataSM('beforefirstshowfired')) {
						$sub.dataSM('beforefirstshowfired', true);
						if (this.$root.triggerHandler('beforefirstshow.smapi', $sub[0]) === false) {
							return;
						}
					}
					if (this.$root.triggerHandler('beforeshow.smapi', $sub[0]) === false) {
						return;
					}
					this.menuFixLayout($sub);
					$sub.stop(true, true);
					if (!$sub.is(':visible')) {
						// set z-index - for IE < 9 set it to the parent LI
						var zIndex = this.getStartZIndex() + (++this.zIndexInc);
						if (IElt9) {
							$sub.parent().css('z-index', zIndex);
						} else {
							$sub.css('z-index', zIndex);
						}
						// highlight parent item
						if (this.opts.keepHighlighted || this.isCollapsible()) {
							$sub.dataSM('parent-a').addClass('highlighted');
						}
						// min/max-width fix - no way to rely purely on CSS as all UL's are nested
						if (this.opts.subMenusMinWidth || this.opts.subMenusMaxWidth) {
							if (!IElt8) {
								$sub.css({ width: 'auto', minWidth: '', maxWidth: '' }).addClass('sm-nowrap');
								if (this.opts.subMenusMinWidth) {
									$sub.css('min-width', this.opts.subMenusMinWidth);
								}
								if (this.opts.subMenusMaxWidth) {
									var noMaxWidth = this.getWidth($sub);
									$sub.css('max-width', this.opts.subMenusMaxWidth);
									if (noMaxWidth > this.getWidth($sub)) {
										$sub.removeClass('sm-nowrap').css('width', this.opts.subMenusMaxWidth);
									}
								}
								// IE6,7
							} else {
								$sub.children().css('styleFloat', 'none');
								if (IE6) {
									$sub.width(this.opts.subMenusMinWidth ? this.opts.subMenusMinWidth : 1)
										.children().children('a').css('white-space', 'nowrap');
								} else { // IE7
									$sub.css({ width: 'auto', minWidth: '', maxWidth: '' }).addClass('sm-nowrap');
									if (this.opts.subMenusMinWidth) {
										$sub.css('min-width', this.opts.subMenusMinWidth);
									}
								}
								if (this.opts.subMenusMaxWidth) {
									var noMaxWidth = $sub.width();
									if (IE6) {
										var maxWidth = $sub.css({ width: this.opts.subMenusMaxWidth, overflowX: 'hidden', overflowY: 'hidden' }).width();
										if (noMaxWidth > maxWidth) {
											$sub.css({ width: maxWidth, overflowX: 'visible', overflowY: 'visible' }).children().children('a').css('white-space', '');
										} else {
											$sub.css({ width: noMaxWidth, overflowX: 'visible', overflowY: 'visible' });
										}
									} else { // IE7
										$sub.css('max-width', this.opts.subMenusMaxWidth);
										if (noMaxWidth > $sub.width()) {
											$sub.removeClass('sm-nowrap').css('width', this.opts.subMenusMaxWidth);
										} else {
											$sub.width(noMaxWidth);
										}
									}
								} else {
									$sub.width($sub.width());
								}
								$sub.children().css('styleFloat', 'left');
							}
						}
						this.menuPosition($sub);
						// insert IE iframe shim
						if ($sub.dataSM('ie-shim')) {
							$sub.dataSM('ie-shim').insertBefore($sub);
						}
						var complete = function() {
							// fix: "overflow: hidden;" is not reset on animation complete in jQuery < 1.9.0 in Chrome when global "box-sizing: border-box;" is used
							$sub.css('overflow', '');
						};
						// if sub is collapsible (mobile view)
						if (this.isCollapsible()) {
							if (this.opts.collapsibleShowFunction) {
								this.opts.collapsibleShowFunction.call(this, $sub, complete);
							} else {
								$sub.show(this.opts.collapsibleShowDuration, complete);
							}
						} else {
							if (this.opts.showFunction) {
								this.opts.showFunction.call(this, $sub, complete);
							} else {
								$sub.show(this.opts.showDuration, complete);
							}
						}
						// save new sub menu for this level
						this.visibleSubMenus[$sub.dataSM('level') - 1] = $sub;
						this.$root.triggerHandler('show.smapi', $sub[0]);
					}
				},
				popupHide: function(noHideTimeout) {
					if (this.hideTimeout) {
						clearTimeout(this.hideTimeout);
						this.hideTimeout = 0;
					}
					var self = this;
					this.hideTimeout = setTimeout(function() {
						self.menuHideAll();
					}, noHideTimeout ? 1 : this.opts.hideTimeout);
				},
				popupShow: function(left, top) {
					if (!this.opts.isPopup) {
						alert('SmartMenus jQuery Error:\n\nIf you want to show this menu via the "popupShow" method, set the isPopup:true option.');
						return;
					}
					if (this.hideTimeout) {
						clearTimeout(this.hideTimeout);
						this.hideTimeout = 0;
					}
					this.menuFixLayout(this.$root);
					this.$root.stop(true, true);
					if (!this.$root.is(':visible')) {
						this.$root.css({ left: left, top: top });
						// IE iframe shim
						this.menuIframeShim(this.$root);
						if (this.$root.dataSM('ie-shim')) {
							this.$root.dataSM('ie-shim').css({ zIndex: this.$root.css('z-index'), width: this.getWidth(this.$root), height: this.getHeight(this.$root), left: left, top: top }).insertBefore(this.$root);
						}
						// show menu
						if (this.opts.showFunction) {
							this.opts.showFunction.call(this, this.$root);
						} else {
							this.$root.show(this.opts.showDuration);
						}
						this.visibleSubMenus[0] = this.$root;
					}
				},
				refresh: function() {
					this.menuHideAll();
					this.$root.find('ul').each(function() {
						var $this = $(this);
						if ($this.dataSM('scroll-arrows')) {
							$this.dataSM('scroll-arrows').remove();
						}
					})
						.removeDataSM('in-mega')
						.removeDataSM('shown-before')
						.removeDataSM('ie-shim')
						.removeDataSM('scroll-arrows')
						.removeDataSM('parent-a')
						.removeDataSM('level')
						.removeDataSM('beforefirstshowfired');
					this.$root.find('a.has-submenu').removeClass('has-submenu')
						.parent().removeDataSM('sub');
					if (this.opts.subIndicators) {
						this.$root.find('span.sub-arrow').remove();
					}
					if (this.opts.markCurrentItem) {
						this.$root.find('a.current').removeClass('current');
					}
					this.subMenus = [];
					this.init(true);
				},
				rootOut: function(e) {
					if (!this.handleEvents() || this.isTouchMode() || e.target == this.$root[0]) {
						return;
					}
					if (this.hideTimeout) {
						clearTimeout(this.hideTimeout);
						this.hideTimeout = 0;
					}
					if (!this.opts.showOnClick || !this.opts.hideOnClick) {
						var self = this;
						this.hideTimeout = setTimeout(function() { self.menuHideAll(); }, this.opts.hideTimeout);
					}
				},
				rootOver: function(e) {
					if (!this.handleEvents() || this.isTouchMode() || e.target == this.$root[0]) {
						return;
					}
					if (this.hideTimeout) {
						clearTimeout(this.hideTimeout);
						this.hideTimeout = 0;
					}
				},
				winResize: function(e) {
					if (!this.handleEvents()) {
						// we still need to resize the disable overlay if it's visible
						if (this.$disableOverlay) {
							var pos = this.$root.offset();
							this.$disableOverlay.css({
								top: pos.top,
								left: pos.left,
								width: this.$root.outerWidth(),
								height: this.$root.outerHeight()
							});
						}
						return;
					}
					// hide sub menus on resize - on mobile do it only on orientation change
					if (!this.isCollapsible() && (!('onorientationchange' in window) || e.type == 'orientationchange')) {
						if (this.activatedItems.length) {
							this.activatedItems[this.activatedItems.length - 1][0].blur();
						}
						this.menuHideAll();
					}
				}
			}
		});

		$.fn.dataSM = function(key, val) {
			if (val) {
				return this.data(key + '_smartmenus', val);
			}
			return this.data(key + '_smartmenus');
		}

		$.fn.removeDataSM = function(key) {
			return this.removeData(key + '_smartmenus');
		}

		$.fn.SmartMenus = function(options) {
			if (typeof options == 'string') {
				var args = arguments,
					method = options;
				Array.prototype.shift.call(args);
				return this.each(function() {
					var smartmenus = $(this).data('smartmenus');
					if (smartmenus && smartmenus[method]) {
						smartmenus[method].apply(smartmenus, args);
					}
				});
			}
			var dom = this;
			$.seaBase.run($.fn.SmartMenus.defaults, options, function(config){
				require.async('Nav/SmartMenus/css/'+config.theme+'/'+config.theme+'.css', function(){
					$(dom).addClass('sm '+config.theme);
					return dom.each(function() {
						new $.SmartMenus(dom, config);
					});
				});
			});
		}

		// default settings
		$.fn.SmartMenus.defaults = {
			isPopup:		false,		// is this a popup menu (can be shown via the popupShow/popupHide methods) or a permanent menu bar
			mainMenuSubOffsetX:	0,		// pixels offset from default position
			mainMenuSubOffsetY:	0,		// pixels offset from default position
			subMenusSubOffsetX:	0,		// pixels offset from default position
			subMenusSubOffsetY:	0,		// pixels offset from default position
			subMenusMinWidth:	'10em',		// min-width for the sub menus (any CSS unit) - if set, the fixed width set in CSS will be ignored
			subMenusMaxWidth:	'20em',		// max-width for the sub menus (any CSS unit) - if set, the fixed width set in CSS will be ignored
			subIndicators: 		true,		// create sub menu indicators - creates a SPAN and inserts it in the A
			subIndicatorsPos: 	'prepend',	// position of the SPAN relative to the menu item content ('prepend', 'append')
			subIndicatorsText:	'+',		// [optionally] add text in the SPAN (e.g. '+') (you may want to check the CSS for the sub indicators too)
			scrollStep: 		30,		// pixels step when scrolling long sub menus that do not fit in the viewport height
			scrollInterval:		30,		// interval between each scrolling step
			scrollAccelerate:	true,		// accelerate scrolling or use a fixed step
			showTimeout:		250,		// timeout before showing the sub menus
			hideTimeout:		500,		// timeout before hiding the sub menus
			showDuration:		0,		// duration for show animation - set to 0 for no animation - matters only if showFunction:null
			showFunction:		null,		// custom function to use when showing a sub menu (the default is the jQuery 'show')
			// don't forget to call complete() at the end of whatever you do
			// e.g.: function($ul, complete) { $ul.fadeIn(250, complete); }
			hideDuration:		0,		// duration for hide animation - set to 0 for no animation - matters only if hideFunction:null
			hideFunction:		function($ul, complete) { $ul.fadeOut(200, complete); },	// custom function to use when hiding a sub menu (the default is the jQuery 'hide')
			// don't forget to call complete() at the end of whatever you do
			// e.g.: function($ul, complete) { $ul.fadeOut(250, complete); }
			collapsibleShowDuration:0,		// duration for show animation for collapsible sub menus - matters only if collapsibleShowFunction:null
			collapsibleShowFunction:function($ul, complete) { $ul.slideDown(200, complete); },	// custom function to use when showing a collapsible sub menu
			// (i.e. when mobile styles are used to make the sub menus collapsible)
			collapsibleHideDuration:0,		// duration for hide animation for collapsible sub menus - matters only if collapsibleHideFunction:null
			collapsibleHideFunction:function($ul, complete) { $ul.slideUp(200, complete); },	// custom function to use when hiding a collapsible sub menu
			// (i.e. when mobile styles are used to make the sub menus collapsible)
			showOnClick:		false,		// show the first-level sub menus onclick instead of onmouseover (matters only for mouse input)
			hideOnClick:		true,		// hide the sub menus on click/tap anywhere on the page
			keepInViewport:		true,		// reposition the sub menus if needed to make sure they always appear inside the viewport
			keepHighlighted:	true,		// keep all ancestor items of the current sub menu highlighted (adds the 'highlighted' class to the A's)
			markCurrentItem:	false,		// automatically add the 'current' class to the A element of the item linking to the current URL
			markCurrentTree:	true,		// add the 'current' class also to the A elements of all ancestor items of the current item
			rightToLeftSubMenus:	false,		// right to left display of the sub menus (check the CSS for the sub indicators' position)
			bottomToTopSubMenus:	false,		// bottom to top display of the sub menus
			overlapControlsInIE:	true,		// make sure sub menus appear on top of special OS controls in IE (i.e. SELECT, OBJECT, EMBED, etc.)
			css: 'Nav/SmartMenus/css/sm-core-css.css',
			theme: 'sm-blue'
		};

	})(jQuery);
	return jQuery;
});